package org.artifact.core.db;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.artifact.core.context.bytebuf.IByteBuff;
import org.artifact.core.context.bytebuf.IByteBuffFactory;
import org.artifact.core.lang.IServer;
import org.artifact.core.plugin.jedis.JedisPlugin;
import org.artifact.core.util.BitUtil;

import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.db.nosql.redis.RedisDS;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;

public abstract class JedisDao<T extends BaseBean<T>> extends BaseDao<T>{
	
	static final Log log = LogFactory.get(JedisDao.class);
	
	protected Map<Object, String[]> indexKeys = new HashMap<>();
	
	protected Jedis getJedis() {
		return IServer.me().getPlugins().getIPlugin(JedisPlugin.class).getJedis();
	}
	
	@Override
	protected void finishSaveCache() {
		Jedis jedis = getJedis();
		Pipeline p = jedis.pipelined();
		try {
			IByteBuffFactory factory = IServer.me().getContext().getByteBuffFactory();
			IByteBuff byteBuff = factory.allocate();
			for (Entry<Object, T> e : cachedUpdates.entrySet()) {
				byteBuff.clear();
				
				T bean = e.getValue();
				Map map = (Map) bean.write();
				byteBuff.writeMap(map);
				
				// 1.写入数据
				p.hset(toBytes(getTableName()),toBytes(e.getKey()), byteBuff.array());
				// 2.删除历史索引
				removeIndex(p, bean);
				// 3.重新写入索引
				addIndex(p, bean);
			}
			p.sync();
		} finally {
			jedis.close();
		}
	}

	@Override
	protected void finishDeleteCache() {
		Jedis jedis = getJedis();
		Pipeline p = jedis.pipelined();
		try {
			byte[][] fields = new byte[0][cachedUpdates.size()];
			int i = 0;
			for (Entry<Object, T> e : cachedDeletes.entrySet()) {
				fields[i] = toBytes(e.getKey());
				
				removeIndex(p, e.getValue());
				
				i++;
			}
			
			p.hdel(toBytes(getTableName()), fields);
			
			p.sync();
		} finally {
			jedis.close();
		}
	}
	
	@Override
	public boolean existsTable() {
		Jedis jeids = getJedis();
		try {
			return jeids.exists(toBytes(getTableName()));
		} finally {
			jeids.close();
		}
	}
	
	public boolean createTable() {
		return true;
	}

	@Override
	public List<T> findAll() {
		return resultList(()->{
			List<T> list = new ArrayList<>();
			Jedis jedis = getJedis();
			IByteBuffFactory factory = IServer.me().getContext().getByteBuffFactory();
			try {
				Map<byte[], byte[]> all = jedis.hgetAll(toBytes(getTableName()));
				
				Map<Integer, Object> map = null;
				IByteBuff byteBuff = null;
				for (Entry<byte[], byte[]> e : all.entrySet()) {
					byteBuff = factory.wrap(e.getValue());
					map = byteBuff.readMap();
					if(map!=null) {
						list.add(createEntity().read(map));
					}
				}
			} finally {
				jedis.close();
			}
			return list;
		}, t -> true);
	}

	@Override
	public T findByPrimaryKey(Object primaryKey) {
		return resultFirst(()->{
			Jedis jedis = getJedis();
			IByteBuffFactory factory = IServer.me().getContext().getByteBuffFactory();
			IByteBuff byteBuff = null;
			try {
				byte[] data = jedis.hget(toBytes(getTableName()), toBytes(primaryKey));
				if (data != null){
					byteBuff = factory.wrap(data);
					Map<Integer, Object> map = byteBuff.readMap();
					return createEntity().read(map);
				}
			} finally {
				byteBuff = null;
				jedis.close();
			}
			
			return null;
		}, t -> ObjectUtil.equal(t.pk(), primaryKey)); 
	}
	
	public byte[] toBytes(Object obj) {
		byte[] bytes = null;
		if (obj instanceof String) {
			String v = (String) obj;
			try {
				bytes = v.getBytes(CharsetUtil.UTF_8);
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
		}else if(obj instanceof Integer){
			int v = (int) obj;
			bytes = new byte[4];
			BitUtil.putInt(bytes, 0, v);
		}else if(obj instanceof Long){
			long v = (long) obj;
			bytes = new byte[8];
			BitUtil.putLong(bytes, 0, v);
		}
		return bytes;
	}

	protected abstract void addIndex(Pipeline p,T t);
	
	protected abstract void removeIndex(Pipeline p,T t);
	
	protected abstract String[] getIndexKey(T t);
}
